//
//  NetworkingManager.swift
//  XileLife
//
//  Created by xiaoyuan on 2020/4/2.
//  Copyright © 2020 寻宝天行. All rights reserved.
//

/*
 Decodable文档
 https://developer.apple.com/documentation/swift/decodable
 */

import Foundation
import Moya
import HandyJSON
import Alamofire
import ObjectMapper

public struct NetworkingManager {
    
    @discardableResult
    public static func get<R>(_ url: String,
                              parameters: [String : Any]? = nil,
                              headers: [String : String]? = nil,
                              callbackQueue: DispatchQueue? = DispatchQueue.main,
                              responseType: R.Type,
                              progress: ProgressBlock? = .none,
                              completion: @escaping CompletionHandler<R>) -> Cancellable where R: Responseable {
            
            let network = NetworkingSession<CommonAPI>()
            return network.request(.get(url,
                                        parameters: parameters,
                                        headers: headers),
                                   responseType: R.self,
                                   callbackQueue: callbackQueue,
                                   progress: progress,
                                   completion: completion)
    }
    
    
    @discardableResult
    public static func post<R>(_ url: String,
                               parameters: [String : Any]? = nil,
                               headers: [String : String]? = nil,
                               callbackQueue: DispatchQueue? = DispatchQueue.main,
                               responseType: R.Type,
                               progress: ProgressBlock? = .none,
                               completion: @escaping CompletionHandler<R>) -> Cancellable where R: Responseable {
        
        let network = NetworkingSession<CommonAPI>()
        return network.request(.post(url,
                                     parameters: parameters,
                                     headers: headers),
                               responseType: R.self,
                               callbackQueue: callbackQueue,
                               progress: progress,
                               completion: completion)
    }
}

public extension NetworkingManager {
    @discardableResult
    static func get(_ url: String,
                              parameters: [String : Any]? = nil,
                              headers: [String : String]? = nil,
                              callbackQueue: DispatchQueue? = DispatchQueue.main,
                              progress: ProgressBlock? = .none,
                              completion: @escaping CompletionHandler<ResponseModel<NormalModel>>) -> Cancellable {
            
            let network = NetworkingSession<CommonAPI>()
            return network.request(.get(url, parameters: parameters, headers: headers), callbackQueue: callbackQueue, progress: progress, completion: completion)
    }
    
    @discardableResult
    static func get<M>(_ url: String,
                              parameters: [String : Any]? = nil,
                              headers: [String : String]? = nil,
                              model: M.Type,
                              callbackQueue: DispatchQueue? = DispatchQueue.main,
                              progress: ProgressBlock? = .none,
                              completion: @escaping CompletionHandler<ResponseModel<M>>) -> Cancellable where M: HandyJSON {
            
            let network = NetworkingSession<CommonAPI>()
            return network.request(.get(url, parameters: parameters, headers: headers),
                                   model: M.self,
                                   callbackQueue: callbackQueue,
                                   progress: progress,
                                   completion: completion)
    }
    
    @discardableResult
    static func post(_ url: String,
                               parameters: [String : Any]? = nil,
                               headers: [String : String]? = nil,
                               callbackQueue: DispatchQueue? = DispatchQueue.main,
                               progress: ProgressBlock? = .none,
                               completion: @escaping CompletionHandler<ResponseModel<NormalModel>>) -> Cancellable {
        
        let network = NetworkingSession<CommonAPI>()
        return network.request(.post(url, parameters: parameters, headers: headers),
                               callbackQueue: callbackQueue,
                               progress: progress,
                               completion: completion)
    }
    
    @discardableResult
    static func post<M>(_ url: String,
                               parameters: [String : Any]? = nil,
                               headers: [String : String]? = nil,
                               callbackQueue: DispatchQueue? = DispatchQueue.main,
                               model: M.Type,
                               progress: ProgressBlock? = .none,
                               completion: @escaping CompletionHandler<ResponseModel<M>>) -> Cancellable where M: HandyJSON {
        
        let network = NetworkingSession<CommonAPI>()
        return network.request(.post(url, parameters: parameters, headers: headers),
                               model: M.self,
                               callbackQueue: callbackQueue,
                               progress: progress,
                               completion: completion)
    }
    
    static func download(url: URL, to destination: (() -> URL)? = nil, progress: ((_ progressValue: Float) -> Void)?, success: @escaping ((_ destinationURL: URL) -> Void), failure: @escaping ((_ error: Error) -> Void)) {
        
        let request = URLRequest(url: url)
        AF.download(request) { (tmpURL, response) -> (destinationURL: URL, options: DownloadRequest.Options) in
            if let destination = destination {
                return (destination(), [.removePreviousFile,.createIntermediateDirectories])
            }
            let cachesPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
            let fileUrl = cachesPath?.appendingPathComponent("DCPrivateCache/\(response.suggestedFilename ?? "unknow")")
            return (fileUrl!,[.removePreviousFile,.createIntermediateDirectories])
        }.response(completionHandler: { (downloadResponse) in
            if let error = downloadResponse.error {
                failure(error)
            }
            else {
                success(downloadResponse.value!!)
            }
        }) .downloadProgress { (downloadProgress) in
            let pValue = Float(downloadProgress.completedUnitCount) / Float(downloadProgress.totalUnitCount)
            print("下载的进度：\(pValue)")
            if let progress = progress {
                progress(pValue)
            }
        }
    }
}

private enum CommonAPI {
    case get(String, parameters: [String: Any]?, headers: [String : String]?)
    case post(String, parameters: [String: Any]?, headers: [String : String]?)
}

extension CommonAPI: MyApiTargetType {
    public var baseURL: URL {
        switch self {
        case .get(let url, _ , _):
            return URL(string: url)!
        case .post(let url, _ , _):
            return URL(string: url)!
        }
    }
    
    public var headers: [String : String]? {
        return nil
    }
    
    public var path: String { return "" }
    
    public var method: Moya.Method {
        switch self {
        case .get(_ , _ , _):
            return .get
        case .post(_ , _ , _):
            return .post
        }
    }
    
    public var parameters: [String: Any] {
        var parameters:[String:Any] = [:]
        var newParameters: [String: Any]?
        
        switch self {
        case .get(_, let parameters, _):
            newParameters = parameters
        case .post(_, let parameters, _):
            newParameters = parameters
        }
        
        if let temp = newParameters {
            temp.forEach { (arg) in
                let (key, value) = arg
                parameters[key] = value
            }
        }
        return parameters
    }
}
